package com.swq.java.code.chapter13.code35;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock和Condition演示
 */
public class SyncTest {
    public static void main(String args[]) {
        Stack stack1 = new Stack("stack1");
        Producer producer1 = new Producer(stack1, "producer1");
        Consumer consumer1 = new Consumer(stack1, "consumer1");
        Consumer consumer2 = new Consumer(stack1, "consumer2");
    }
}

/**
 * 生产者线程
 */
class Producer extends Thread {
    private Stack theStack;

    public Producer(Stack s, String name) {
        super(name);
        theStack = s;
        start();  //启动自身生产者线程
    }

    public void run() {
        String goods;
        Lock stackLock = theStack.getLock();
        for (int i = 0; i < 200; i++) {
            stackLock.lock();
            try {
                goods = "goods" + (theStack.getPoint() + 1);
                theStack.push(goods);
            } finally {
                stackLock.unlock();
            }

            System.out.println(getName() + ": push " + goods + " to " + theStack.getName());
            sleep(100);
        }
    }

    public void sleep(int m) {
        try {
            super.sleep(m);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 消费者线程
 */
class Consumer extends Thread {
    private Stack theStack;

    public Consumer(Stack s, String name) {
        super(name);
        theStack = s;
        start();  //启动自身消费者线程
    }

    public void run() {
        String goods;
        for (int i = 0; i < 200; i++) {
            goods = theStack.pop();
            System.out.println(getName() + ": pop " + goods + " from " + theStack.getName());
            yield();
        }
    }
}

/**
 * 堆栈
 */
class Stack {
    private String name;
    private String[] buffer = new String[100];
    int point = -1;
    private final Lock stackLock;
    private Condition condition;

    public Stack(String name) {
        this.name = name;
        stackLock = new ReentrantLock();
        condition = stackLock.newCondition();
    }

    public String getName() {
        return name;
    }

    public Lock getLock() {
        return stackLock;
    }

    public int getPoint() {
        stackLock.lock();
        try {
            return point;
        } finally {
            stackLock.unlock();
        }
    }

    public String pop() {
        stackLock.lock();
        try {
            condition.signalAll();

            while (point == -1) {
                System.out.println(Thread.currentThread().getName() + ": wait");
                condition.await();
            }

            String goods = buffer[point];
            buffer[point] = null;
            Thread.yield();
            point--;

            return goods;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            stackLock.unlock();
        }
    }

    public void push(String goods) {
        stackLock.lock();
        try {
            condition.signalAll();

            while (point == buffer.length - 1) {
                System.out.println(Thread.currentThread().getName() + ": wait");
                condition.await();
            }
            point++;
            Thread.yield();
            buffer[point] = goods;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            stackLock.unlock();
        }
    }
}